home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / corelib / ncbierr.c < prev    next >
Text File  |  1996-07-05  |  45KB  |  1,728 lines

  1. /*   ncbierr.c
  2. * ===========================================================================
  3. *
  4. *                            PUBLIC DOMAIN NOTICE
  5. *               National Center for Biotechnology Information
  6. *
  7. *  This software/database is a "United States Government Work" under the
  8. *  terms of the United States Copyright Act.  It was written as part of
  9. *  the author's official duties as a United States Government employee and
  10. *  thus cannot be copyrighted.  This software/database is freely available
  11. *  to the public for use. The National Library of Medicine and the U.S.
  12. *  Government have not placed any restriction on its use or reproduction.
  13. *
  14. *  Although all reasonable efforts have been taken to ensure the accuracy
  15. *  and reliability of the software and data, the NLM and the U.S.
  16. *  Government do not and cannot warrant the performance or results that
  17. *  may be obtained by using this software or data. The NLM and the U.S.
  18. *  Government disclaim all warranties, express or implied, including
  19. *  warranties of performance, merchantability or fitness for any particular
  20. *  purpose.
  21. *
  22. *  Please cite the author in any work or product based on this material.
  23. *
  24. * ===========================================================================
  25. *
  26. * File Name:  ncbierr.c
  27. *
  28. * Author:  Schuler, Sirotkin (UserErr stuff)
  29. *
  30. * Version Creation Date:   9-19-91
  31. *
  32. * $Revision: 2.32 $
  33. *
  34. * File Description:  Error handling functions
  35. *
  36. * Modifications:
  37. * --------------------------------------------------------------------------
  38. * Date     Name        Description of modification
  39. * -------  ----------  -----------------------------------------------------
  40. * 04-13-93 Schuler     Added TRACE, VERIFY, ASSERT macros.
  41. * 04-13-93 Schuler     Added AbnormalExit function.
  42. * 05-11-93 Schuler     Added ErrSetHandler function.
  43. * 05-21-93 Schuler     Remove PROTO from ErrSetHandler implementation (oops)
  44. * 05-26-93 Schuler     Nlm_TraceStr flushes stderr, if appropriate
  45. * 07-26-93 Schuler     Moved globals into a (per-app) context struct
  46. * 07-26-93 Schuler     Fixed ErrClear()
  47. * 11-15-93 Schuler     Fixed double error reporting problem in ErrPost()
  48. * 12-13-93 Schuler     Message file support and lots of other changes.
  49. * 12-22-93 Schuler     Added log_level setting (min. severity to log)
  50. * 01-03-94 Schuler     Added ErrSaveOptions and ErrRestoreOptions
  51. * 01-04-94 Schuler     Added code to get settings from NCBI config file
  52. * 01-07-94 Schuler     Fixed bug in ErrMsgRoot_Read().
  53. * 01-10-94 Schuler     Added check for not_avail in ErrMsgRoot_fopen()
  54. * 01-10-94 Schuler     Fixed bug in ErrGetMsgRoot()
  55. * 01-13-94 Schuler     Added an handful of typecasts to supress warnings
  56. * 01-23-94 Schuler     Fixed bug in ErrSetOpts
  57. * 02-02-94 Schuler     ErrOpt structure has fields for actopt, logopt
  58. * 02-02-94 Schuler     Revisions related to change in message file format
  59. * 02-02-94 Schuler     Use TEST_BITS macro throughout
  60. * 02-02-94 Schuler     Improved back.compatability of ErrGetOpts/ErrSetOpts
  61. * 02-10-94 Schuler     Workaround for obsolete ERR_IGNORE option
  62. * 02-10-94 Schuler     Fixed bug in ErrSetFatalLevel
  63. * 02-18-94 Schuler     Fix to deal with possibility of userstrings being NULL
  64. * ==========================================================================
  65. */
  66.  
  67.  
  68. #include <ncbi.h>
  69. #include <ncbiwin.h>
  70.  
  71. char * g_corelib = "CoreLib";
  72. #undef  THIS_MODULE
  73. #define THIS_MODULE g_corelib
  74.  
  75. static char *_filename = __FILE__;
  76. #undef  THIS_FILE
  77. #define THIS_FILE _filename
  78.  
  79. #ifdef VAR_ARGS
  80. #include <varargs.h>
  81. #define VSPRINTF(buff,fmt)         \
  82.     {                              \
  83.         va_list args;              \
  84.         va_start(args);            \
  85.         vsprintf(buff,fmt,args);   \
  86.         va_end(args);              \
  87.     }
  88.  
  89. #else
  90. #include <stdarg.h>
  91. #define VSPRINTF(buff,fmt)         \
  92.     {                              \
  93.         va_list args;              \
  94.         va_start(args,fmt);        \
  95.         vsprintf(buff,fmt,args);   \
  96.         va_end(args);              \
  97.     }
  98. #endif
  99.  
  100.  
  101. struct AppErrInfo
  102. {
  103.     ErrDesc desc;
  104.     ErrOpts opts;
  105.     ErrHookProc    hook;
  106.     unsigned long ini_mask;
  107.     unsigned long ini_bits;
  108.     unsigned int any_error :1;
  109.     unsigned int err_formatted :1;
  110.     unsigned int debug_mode :1;
  111.     Nlm_sizeT ustrcnt;
  112.     Nlm_sizeT ustrlen;
  113.     char  logfile[PATH_MAX];
  114.     char  msgpath[PATH_MAX];
  115.     ErrMsgRoot *idxlist;
  116.     ErrMsgNode *node;
  117. };
  118.  
  119. typedef struct AppErrInfo *AppErrInfoPtr;
  120.  
  121.  
  122. static char * _szPropKey = "_AppErrInfo";
  123. static char * _szSevKey [] = { "", "SEV_INFO", "SEV_WARNING", "SEV_ERROR", "SEV_FATAL" };
  124. static char * _szSevDef [] = { "", "NOTE:", "WARNING:", "ERROR:", "FATAL ERROR:" };
  125. static char * _szSeverity[5];
  126. static char _busy;
  127.  
  128. char *GetScratchBuffer PROTO((void));
  129. static AppErrInfoPtr GetAppErrInfo PROTO((void));
  130.  
  131. #define FUSE_BITS(inf)  ( ((inf)->ini_bits & (inf)->ini_mask) | ((inf)->opts.flags & ~((inf)->ini_mask)) )
  132. #define TEST_BITS(inf,x)  (FUSE_BITS(inf) & (x))
  133.  
  134.  
  135.  
  136. /***************************************************************************\
  137. |                           POSTING AN ERROR                                |
  138. \***************************************************************************/
  139.  
  140. /*-------------------------------------------------------------------------
  141. * ErrPost   [Schuler]
  142. *
  143. * MODIFICATIONS:
  144. * 04-13-93 Schuler  
  145. * 07-26-93 Schuler   Modified to use AppErrInfo struct
  146. * 11-15-93 Schuler   Fixed bug that resulted in reporting errors twice
  147. * 12-14-93 Schuler   Modified to call the new ErrPostStr function
  148. * 01-14-94 Schuler   Check _busy flag
  149. */
  150.  
  151. #ifdef VAR_ARGS
  152. void CDECL  Nlm_ErrPost (context, errcode, fmt, va_alist)
  153.   int context;
  154.   int errcode;
  155.   const char *fmt;
  156.   va_dcl
  157. #else
  158. void CDECL Nlm_ErrPost (int context, int errcode, const char *fmt, ...)
  159. #endif
  160. {
  161.     AppErrInfoPtr info = GetAppErrInfo();
  162.     ErrSev sev = (context==CTX_DEBUG) ? SEV_INFO : SEV_FATAL;
  163.     if (_busy)
  164.     {
  165.         if (sev == SEV_FATAL)  AbnormalExit(1);
  166.         return;
  167.     }
  168.     info->desc.context = context;
  169.     VSPRINTF(info->desc.errtext,fmt);
  170.     info->err_formatted =1;
  171.     (void)Nlm_ErrPostStr(sev,errcode,0,NULL);
  172. }
  173.  
  174.  
  175. /*-------------------------------------------------------------------------
  176. *    ErrPostEx   [Schuler, 12-13-93]
  177. *
  178. * MODIFICATIONS:
  179. * 01-14-94 Schuler   Check _busy flag
  180. */
  181.  
  182. #ifdef VAR_ARGS
  183. int CDECL  Nlm_ErrPostEx (sev, lev1, lev2, fmt, va_alist)
  184.   ErrSev sev;
  185.   int lev1;
  186.   int lev2;
  187.   const char *fmt;
  188.   va_dcl
  189. #else
  190. int CDECL Nlm_ErrPostEx (ErrSev sev, int lev1, int lev2, const char *fmt, ...)
  191. #endif
  192. {
  193.     AppErrInfoPtr info = GetAppErrInfo();
  194.     if (_busy)
  195.     {
  196.         if (sev == SEV_FATAL)  AbnormalExit(1);
  197.         return 0;
  198.     }
  199.     VSPRINTF(info->desc.errtext,fmt);
  200.     info->err_formatted =1;
  201.     return Nlm_ErrPostStr(sev,lev1,lev2,NULL);
  202. }
  203.  
  204.  
  205. /*-------------------------------------------------------------------------
  206. * ErrPostStr   [Schuler, 12-13-93]
  207. *
  208. * MODIFICATIONS:
  209. * 12-22-93 Schuler   Only logs error if severity >= log_level
  210. * 01-14-94 Schuler   Check _busy flag
  211. * 02-03-94 Schuler   Use severity from message file if there is one
  212. * 02-10-94 Schuler   Workaround for obsolete ERR_IGNORE option
  213. * 02-18-94 Schuler   Check for NULL user strings before printing
  214. */
  215.  
  216. int LIBCALL Nlm_ErrPostStr (ErrSev sev, int lev1, int lev2, const char *str)
  217. {
  218.     AppErrInfoPtr info = GetAppErrInfo();
  219.     ErrMsgRootPtr root  = NULL;
  220.     ErrMsgNodePtr node1 = NULL;
  221.     ErrMsgNodePtr node2 = NULL;
  222.     int severity = sev;
  223.     
  224.     if (_busy)
  225.     {
  226.         if (sev == SEV_FATAL)  AbnormalExit(1);
  227.         return 0;
  228.     }
  229.  
  230.     /* ----- Fill in the fields of the error descriptor ----- */
  231.     info->any_error = 1;
  232.     info->desc.severity = sev;
  233.     info->desc.errcode = lev1;
  234.     info->desc.subcode = lev2;
  235.  
  236.     /* ----- set up the root and node for message file ----- */
  237.     if ( (info->desc.module[0] != '\0') )
  238.     {
  239.         root = ErrGetMsgRoot(info->desc.module);
  240.         for (node1=root->list; node1; node1=node1->next)
  241.         {
  242.             if (node1->code == info->desc.errcode)
  243.             {
  244.                 for (node2=node1->list; node2; node2=node2->next)
  245.                     if (node2->code == info->desc.subcode)
  246.                         break;
  247.                 break;
  248.             }
  249.         }
  250.     }
  251.     info->desc.root = root;
  252.     info->desc.node = node2 ? node2 : node1;
  253.     if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE)
  254.         severity = info->desc.node->sev;
  255.  
  256.     /* ----- format some strings ----- */
  257.     if (!info->err_formatted)
  258.     {
  259.         info->desc.errtext[0] = '\0';
  260.         if (str != NULL)
  261.             strncat(info->desc.errtext,str,ERRTEXT_MAX);
  262.     }
  263.     if (info->desc.context != 0)
  264.         sprintf(info->desc.codestr,"[%03d:%03d] ",info->desc.context,info->desc.errcode);
  265.     else if (node1 != NULL && (TEST_BITS(info,EO_XLATE_CODES)))
  266.     {
  267.         if (node2 != NULL)
  268.             sprintf(info->desc.codestr,"[%s.%s] ",node1->name,node2->name);
  269.         else
  270.             sprintf(info->desc.codestr,"[%s] ",node1->name);
  271.     }
  272.     else
  273.     {
  274.         sprintf(info->desc.codestr,"[%03d.%03d] ",info->desc.errcode,info->desc.subcode);
  275.     }
  276.  
  277.  
  278.     /* ----- Log the error according to the current options ----- */
  279.     if (severity >= info->opts.log_level)
  280.     {
  281.         
  282. #ifdef WIN_DUMB
  283.         if (TEST_BITS(info,EO_LOGTO_STDOUT))
  284.             fflush(stderr);
  285.         if (TEST_BITS(info,EO_LOGTO_STDERR))
  286.             fflush(stdout);
  287. #endif
  288.  
  289.         if (TEST_BITS(info,EO_LOG_SEVERITY))
  290.         {
  291.             ErrLogPrintf("%s ",_szSeverity[severity]);
  292.         }
  293.         if (TEST_BITS(info,EO_LOG_CODES))
  294.         {
  295.             if (info->desc.module[0])
  296.                 ErrLogPrintf("%s ",info->desc.module);
  297.             ErrLogPrintStr(info->desc.codestr);
  298.         }
  299.         if (TEST_BITS(info,EO_LOG_FILELINE))
  300.         {
  301.             ErrLogPrintf("{%s, line %d} ",info->desc.srcfile,info->desc.srcline);
  302.         }
  303.         if (TEST_BITS(info,EO_LOG_USERSTR))
  304.         {
  305.             const ValNode *node;
  306.             for (node=info->desc.userstr; node; node=node->next)
  307.             {
  308.                 if (node->data.ptrvalue != NULL)
  309.                     ErrLogPrintf("%s ",(char*)node->data.ptrvalue);
  310.             }
  311.         }
  312.         if (TEST_BITS(info,EO_LOG_ERRTEXT))
  313.         {
  314.             ErrLogPrintStr(info->desc.errtext);
  315.             ErrLogPrintStr("\n");
  316.         }
  317.         if (node1 != NULL && TEST_BITS(info,EO_LOG_MSGTEXT))
  318.         {
  319.             ErrMsgNodePtr node = (node2==NULL) ? node1 : node2;
  320.             const char *text = ErrGetExplanation(root,node);
  321.             ErrLogPrintStr(text);
  322.         }
  323.     }
  324.  
  325.     /* ----- Workaround for obsolete ERR_IGNORE option ----- */
  326.     if (info->opts.actopt == ERR_IGNORE)
  327.     {
  328.         ErrClear();
  329.         return ANS_NONE;
  330.     }
  331.  
  332.     /* ----- Call the user-installed hook function if there is one ----- */
  333.     if (info->hook != NULL)
  334.     {
  335.         int retval;
  336.         if ((retval = (*info->hook)(&(info->desc))) != 0)
  337.         {
  338.             ErrClear();
  339.             return retval;        
  340.         }
  341.     }
  342.  
  343.     /* ----- If not already handled, perform default error handling ----- */
  344.     if (severity >= info->opts.msg_level || severity >= info->opts.die_level)
  345.         return ErrShow();
  346.     
  347.     return ANS_OK;
  348. }
  349.  
  350. /*-------------------------------------------------------------------------
  351. *    ErrLogPrintf   [Schuler, 12-13-93]
  352. *
  353. *  Formats the string and passes it along to ErrLogPrintStr().
  354. */
  355. #ifdef VAR_ARGS
  356. void CDECL Nlm_ErrLogPrintf (fmt, va_alist)
  357.   const char *fmt;
  358.   va_dcl
  359. #else
  360. void CDECL Nlm_ErrLogPrintf (const char *fmt, ...)
  361. #endif
  362. {
  363.     char *buffer = GetScratchBuffer();
  364.     VSPRINTF(buffer,fmt);
  365.     ErrLogPrintStr(buffer);
  366. }
  367.  
  368. /*-------------------------------------------------------------------------
  369. * ErrLogPrintStr   [Schuler, 12-13-93]
  370. *
  371. * Sends a string of text to whichever output streams have been enabled
  372. * for error logging (stderr, trace, or logfile).
  373. *
  374. * MODIFICATIONS
  375. * 12-15-93 Schuler   Added fflush(stdout) before writing to stderr.
  376. */
  377. void LIBCALL Nlm_ErrLogPrintStr (const char *str)
  378. {
  379.     AppErrInfoPtr info = GetAppErrInfo();
  380.                                                    
  381.     if (str != NULL)
  382.     {
  383.         size_t bytes = strlen(str);
  384.         if (bytes > 0)
  385.         {
  386.             if (TEST_BITS(info,EO_LOGTO_STDOUT))
  387.             {
  388.                 fflush(stderr);
  389.                 fwrite(str,1,bytes,stdout);
  390.                 fflush(stdout);
  391.             }
  392.             if (TEST_BITS(info,EO_LOGTO_STDERR))
  393.             {
  394.                 fflush(stdout);
  395.                 fwrite(str,1,bytes,stderr);
  396.                 fflush(stderr);
  397.             }
  398.             if (TEST_BITS(info,EO_LOGTO_TRACE))
  399.             {
  400.                 Nlm_TraceStr(str);
  401.             }
  402.             if (TEST_BITS(info,EO_LOGTO_USRFILE))
  403.             {
  404.                 FILE *fd = FileOpen(info->logfile,"a+");
  405.                 if (fd != NULL)
  406.                 {
  407.                     fwrite(str,1,bytes,fd);
  408.                     FileClose(fd);
  409.                 }
  410.             }
  411.         }
  412.     }
  413. }
  414.  
  415. /*-------------------------------------------------------------------------
  416. *    ErrSetContext   [Schuler, 12-13-93]
  417. *
  418. *  NOTE: Don't call this function directly 
  419. *
  420. * MODIFICATIONS
  421. * 02-03-94 Schuler   Return 1 if busy
  422. */
  423. int LIBCALL Nlm_ErrSetContext (const char *ctx, const char *fname, int line, int db)
  424. {
  425.     AppErrInfoPtr info;
  426.     
  427.     if (_busy)  return 1;
  428.     info = GetAppErrInfo();
  429.     
  430.     info->desc.module[0] = '\0';
  431.     if (ctx != NULL)
  432.         strncat(info->desc.module,ctx,MODSTR_MAX);
  433.     info->desc.srcfile[0] = '\0';
  434.     if (fname != NULL)
  435.     {
  436.         const char *p;
  437.         if ((p = strrchr(fname,DIRDELIMCHR)) != NULL)
  438.             ++p;
  439.         else
  440.             p = fname;
  441.         strncat(info->desc.srcfile,p,SRCFILE_MAX);
  442.     }
  443.     info->desc.srcline = line;
  444.     info->debug_mode = (unsigned)db;
  445.     return 0;
  446. }
  447.  
  448.  
  449. /***************************************************************************\
  450. |                     FETCHING AND REPORTING ERRORS                         |
  451. \***************************************************************************/
  452.  
  453. /*-------------------------------------------------------------------------
  454.  * ErrFetch  [Schuler]
  455.  *
  456.  * MODIFICATIONS
  457.  * 07-26-93 Schuler   Modified to use AppErrInfo struct
  458.  */
  459. int LIBCALL  Nlm_ErrFetch (ErrDesc *err)
  460. {
  461.     if (!Nlm_ErrCopy(err))
  462.         return FALSE;
  463.        ErrClear();
  464.     return TRUE;
  465. }
  466.  
  467.  
  468. /*-------------------------------------------------------------------------
  469.  * ErrCopy  [Schuler, 07-26-93]
  470.  *
  471.  * MODIFICATIONS:
  472.  * 12-12-93 Schuler   Check info->any_error flag before copying
  473.  */
  474. int LIBCALL  Nlm_ErrCopy (ErrDesc FAR *err)
  475. {
  476.     AppErrInfoPtr info = GetAppErrInfo();
  477.     if (info->any_error)
  478.     {
  479.         if (err != NULL)
  480.             memcpy((void*)err,(void*)&(info->desc),sizeof(ErrDesc));
  481.         return TRUE;
  482.     }
  483.     return FALSE;
  484. }
  485.  
  486.  
  487. /*-------------------------------------------------------------------------
  488.  * ErrClear   [Schuler, 07-26-93]
  489.  *
  490.  * MODIFICATIONS
  491.  * 12-13-93 Schuler   Just clear flags instead of zeroing the whole struct.
  492.  */
  493. void LIBCALL Nlm_ErrClear ()
  494. {
  495.     AppErrInfoPtr info = GetAppErrInfo();
  496.     info->any_error = 0;
  497.     info->err_formatted = 0;
  498.     info->debug_mode = 0;
  499.     info->desc.context = 0;
  500. }
  501.  
  502. /*-------------------------------------------------------------------------
  503. * ErrShow   [Schuler]
  504. *
  505. * MODIFICATIONS
  506. * 12-13-93 Schuler   Rewritten to use msg_level and die_level settings.
  507. * 12-14-93 Schuler   Now returns the result of Message().
  508. * 12-15-93 Schuler   Added proper handling of Abort/Retry/Ignore choice.
  509. * 12-21-93 Schuler   Added special case for WIN_DUMB + logging to stderr
  510. * 12-24-94 Schuler   Changed Message to MsgAlert
  511. * 12-27-94 Schuler   Removed Beep() because MsgAlert is taking care of it
  512. * 01-31-94 Schuler   Put Beep() back because MsgAlert is no longer doing it.
  513. * 02-03-94 Schuler   Now honors all EO_MSG_... option flags.
  514. * 02-18-94 Schuler   Check for NULL user strings before copying.
  515. *
  516. * TO DO
  517. * - allocate buffer for the message
  518. * - honor option flags
  519. */
  520. int LIBCALL Nlm_ErrShow ()
  521. {
  522.     AppErrInfoPtr info = GetAppErrInfo();
  523.     if (info->any_error)
  524.     {
  525.         int answer = ANS_OK;
  526.         int severity = info->desc.severity;
  527.         if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE)
  528.             severity = info->desc.node->sev;
  529.         if (severity >= info->opts.msg_level)
  530.         {
  531.             size_t bytes;
  532.             const char *caption = (char*)GetAppProperty("AppName");
  533.             char *message;
  534.             const char *msgtext;
  535.             char *p;
  536.             MsgKey key;
  537.  
  538.             /* ----- beep if requested ----- */
  539.             if (TEST_BITS(info,EO_BEEP))
  540.                 Nlm_Beep();
  541.                 
  542.             /* ----- set up the buffer to hold the error message ---- */
  543.             if (TEST_BITS(info,EO_MSG_MSGTEXT))
  544.                 msgtext = ErrGetExplanation(info->desc.root,info->desc.node);
  545.             else
  546.                 msgtext = NULL;
  547.             /* forget about the options for now -- this is the max we would need*/
  548.             bytes = 2 + strlen(info->desc.module)
  549.                 + 32    /* severity string.  TEMPORARY (need to keep track of longest string) */
  550.                 + 2  + strlen(info->desc.codestr)  /* error codes */
  551.                 + 16 + strlen(info->desc.srcfile)  /* source file and line number */
  552.                 + 2  + info->ustrcnt + info->ustrlen  /* user strings */
  553.                 + 2  + strlen(info->desc.errtext)  /* error message */
  554.                 + 2  + ((msgtext) ? strlen(msgtext) : 0);  /* verbose message */
  555.  
  556.             if ((message = (char*)Malloc(bytes)) == NULL)
  557.                 message = info->desc.errtext;
  558.             else 
  559.             {                
  560.                 /* ----- format the message in the buffer ----- */
  561.                 p = message;
  562.                 if (TEST_BITS(info,EO_MSG_SEVERITY))
  563.                 {
  564.                     p = strchr(strcpy(p,_szSeverity[severity]),'\0');
  565.                     *p++ = ' ';
  566.                 }
  567.                 if (TEST_BITS(info,EO_MSG_CODES))
  568.                 {
  569.                     p = strchr(strcpy(p,info->desc.module),'\0');
  570.                     *p++ = ' ';
  571.                     p = strchr(strcpy(p,info->desc.codestr),'\0');
  572.                     *p++ = ' ';
  573.                 }
  574.                 if (TEST_BITS(info,EO_MSG_FILELINE))
  575.                 {
  576.                     /*p = strchr(strcpy(p,info->desc.srcfile),'\0');*/
  577.                     sprintf(p,"{%s line %d} \n",info->desc.srcfile,info->desc.srcline);
  578.                     p = strchr(p,'\0');
  579.                 }
  580.                 if (TEST_BITS(info,EO_MSG_USERSTR))
  581.                 {
  582.                     const ValNode *node;
  583.                     for (node=info->desc.userstr; node; node=node->next)
  584.                     {
  585.                         if (node->data.ptrvalue != NULL)
  586.                         {
  587.                         p = strchr(strcpy(p,(char*)node->data.ptrvalue),'\0');
  588.                         *p++ = ' ';
  589.                         }
  590.                     }
  591.                 }
  592.                 if (TEST_BITS(info,EO_MSG_ERRTEXT))
  593.                 {
  594.                     p = strchr(strcpy(p,info->desc.errtext),'\0');
  595.                 }
  596.                 if (TEST_BITS(info,EO_MSG_MSGTEXT) && msgtext!=NULL)
  597.                 {
  598.                     if (p != message)  *p++ = '\n';
  599.                     p = strchr(strcpy(p,msgtext),'\0');
  600.                 }
  601.             }
  602.                         
  603.             /* ----- show the message ----- */
  604.             if (TEST_BITS(info,EO_PROMPT_ABORT)) key = KEY_ARI;
  605.             else if (TEST_BITS(info,EO_WAIT_KEY)) key = KEY_OK;
  606.             else key = KEY_NONE;
  607.             answer = MsgAlertStr(key,(ErrSev)severity,caption,message);
  608.             
  609.             /* ----- clean up ----- */
  610.             if (message != info->desc.errtext)
  611.                 Free(message);
  612.         }
  613.         
  614.         /* ----- die if appropriate ----- */
  615.         if (severity >= info->opts.die_level)
  616.         {
  617.             int code = info->desc.errcode;
  618.             AbnormalExit(code);
  619.         }
  620.         ErrClear();
  621.         return answer;
  622.     }
  623.     return 0;
  624. }
  625.  
  626.  
  627.  
  628. /***************************************************************************\
  629. |                           MESSAGE FILE SUPPORT                            |
  630. \***************************************************************************/
  631. static ErrMsgRoot * new_ErrMsgRoot PROTO((const char *name));
  632. static void delete_ErrMsgRoot PROTO((ErrMsgRoot *idx));
  633. static FILE * ErrMsgRoot_fopen PROTO((ErrMsgRoot *ctx, const char *mode));
  634. static ErrMsgNode * new_ErrMsgNode PROTO((const char *name, int code, ErrSev sev));
  635. static void delete_ErrMsgNode PROTO((ErrMsgNode *item));
  636. static ErrSev atosev PROTO((const char *sevstr));
  637.  
  638.  
  639. /*-------------------------------------------------------------------------
  640. * new_ErrMsgRoot, delete_ErrMsgRoot   [Schuler, 12-09-93]
  641. *
  642. * Constructor/Destructor for ErrMsgRoot
  643. *
  644. * MODIFICATIONS
  645. *
  646. * TO DO
  647. * - implement destructor
  648. */
  649.  
  650. static ErrMsgRoot * new_ErrMsgRoot (const char *context)
  651. {
  652.     ErrMsgRoot *idx = (ErrMsgRoot*) MemNew(sizeof(ErrMsgRoot));
  653.     if (idx != NULL)
  654.         idx->name = (context) ? StrSave(context) : NULL;
  655.     return idx;
  656. }
  657.  
  658. static void delete_ErrMsgRoot (ErrMsgRoot *idx)
  659. {
  660.     /* not implemented */
  661. }
  662.  
  663. /*-------------------------------------------------------------------------
  664. * ErrMsgRoot_fopen    [Schuler, 12-09-93]
  665. *
  666. * Opens an error message file corresponding to an error context.
  667. *
  668. * MODIFICATIONS
  669. * 01-07-94 Schuler   Looks in the current directory first.
  670. * 01-10-94 Schuler   Uses FileOpen() instead of fopen() (now that 
  671. *                    reentrancy problem is solved).
  672. * TO DO
  673. * remove mode argument -- always use "rb"
  674. */
  675.  
  676. #define MODFNAME_MAX 32
  677.  
  678. static FILE * ErrMsgRoot_fopen (ErrMsgRoot *ctx, const char *mode)
  679. {
  680.     AppErrInfoPtr info = GetAppErrInfo();
  681.     FILE *fd;
  682.     char file[MODFNAME_MAX];
  683.     char path[PATH_MAX];    
  684.     char *p1 = file;
  685.     const char *p2 = ctx->name;
  686.     int i, ch;
  687.  
  688.     /***if (ctx->not_avail)
  689.         return NULL;***/
  690.     
  691.     for (i=0; (ch= *p2++) != 0 && i<MODFNAME_MAX-5; ++i)
  692.     {
  693.         if (isalpha(ch)) ch = tolower(ch);
  694.         *p1++ = (char)ch;
  695.     }
  696.     strcpy(p1,".msg");
  697.  
  698.     if ((fd = FileOpen(file,mode)) == NULL)
  699.     {
  700.         strcpy(path,info->msgpath);
  701.         strncat(path,file,sizeof(path));
  702.         fd = FileOpen(path,mode);
  703.     }
  704.  
  705.     /***if (fd == NULL)
  706.         ctx->not_avail = 1;***/
  707.     return fd;
  708. }
  709.  
  710.  
  711. /*-------------------------------------------------------------------------
  712. * ErrMsgRoot_Read    [Schuler, 12-08-93]
  713. *
  714. * Scans an error message file and builds a data structure that contains
  715. * the mappings between integer error codes and strings as well as file
  716. * offset for the verbose error explanations so they can later be retrieved
  717. * when (or if) needed.
  718. *
  719. * MODIFICATIONS
  720. * 01-07-94 Schuler   Fixed bug that resulted in failure to set the text 
  721. *                    length for the last item in the file.
  722. * 01-10-94 Schuler   Added _busy flag to indicate that we are in the process
  723. *                    of parsing the file.  Otherwise, there is a problem if 
  724. *                    you post an error while attempting to read corelib.msg 
  725. *                    (leads to infinite recursion).
  726. * 01-10-94 Schuler   Added linenumber reporting for all error messages.
  727. * 01-23-94 Schuler   Changed all ErrPostEx to ErrLogPrintf (don't want to 
  728. *                    be posting errors in here -- will recurse ad infinitum).
  729. * 01-21-94 Schuler   Changed fclose() to FileClose()
  730. * 02-02-94 Schuler   Revisions for new file format
  731. *
  732. * TO DO
  733. * Check for missing integer code
  734. * Capture comments
  735. */
  736.  
  737. static ErrSev atosev (const char *sevstr)
  738. {
  739.     int i;
  740.     if (sevstr)  
  741.     {
  742.         for (i=SEV_MIN; i<SEV_MAX; ++i)
  743.         {
  744.             if (strcmp(sevstr,_szSevKey[i]) ==0)
  745.                 return (ErrSev)i;
  746.         }
  747.     }
  748.     return SEV_NONE;
  749. }
  750.  
  751. static int ErrMsgRoot_Read (ErrMsgRoot *idx)
  752. {
  753.     FILE *fd;
  754.  
  755.     if (idx->list != NULL)
  756.         return TRUE;    
  757.     if (_busy /*|| idx->not_avail*/ )
  758.         return FALSE;
  759.     _busy = 1;
  760.         
  761.     if ((fd = ErrMsgRoot_fopen(idx,"rb")) == NULL)
  762.     {
  763.         _busy = 0;
  764.         return FALSE;
  765.     }
  766.     else
  767.     {
  768.         char line[80], *p;
  769.         int linenum=0;
  770.         long tofs, cofs, tmpofs;
  771.         ErrMsgNode *lev1;
  772.         ErrMsgNode *lev2;
  773.         int any_text;
  774.         int any_comt;
  775.         
  776.         /* ----- Look for MODULE line ----- */
  777.         while (fgets(line,sizeof line,fd))
  778.         {
  779.             linenum++;
  780.             if (strchr("#\r\n",line[0]))  continue;
  781.             if (strncmp(line,"MODULE",6) ==0)
  782.             {
  783.                 p = strtok(line," \t\r\n");
  784.                 p = strtok(NULL," \t\r\n");
  785.                 if (strcmp(idx->name,p) !=0)
  786.                     ErrLogPrintf("Context string mismatch (%s vs %s)\n",idx->name,p);
  787.                 break;                 
  788.             }
  789.             else
  790.             {
  791.                 _busy = 0;
  792.                 ErrLogPrintf("Syntax error: \"MODULE\" expected, line %d\n",linenum);
  793.                 return FALSE;
  794.             }
  795.         }
  796.     
  797.         /* ----- Process the rest of the file ----- */
  798.         any_text = any_comt = FALSE;
  799.         lev1 = lev2 = NULL;
  800.         tmpofs = ftell(fd);
  801.         
  802.         while (fgets(line,sizeof line,fd))
  803.         {
  804.             linenum++;
  805.                 
  806.             if (line[0] == '$')
  807.             {
  808.                 if (any_text)
  809.                 {
  810.                     ErrMsgNode *n = lev2 ? lev2 : lev1;
  811.                     if (n != NULL)
  812.                     {
  813.                         n->tofs = tofs;
  814.                         n->tlen = tmpofs - tofs;
  815.                     }
  816.                     any_text = FALSE;
  817.                 }
  818.             }
  819.             
  820.             if (line[0]=='$' && line[1]=='$')
  821.             {
  822.                 /*** NEED ERROR CHECKING HERE ***/
  823.                 char *tok1 = strtok(line+2,", \t\r\n");
  824.                 char *tok2 = strtok(NULL,", \t\r\n");
  825.                 char *tok3 = strtok(NULL,", \t\r\n");
  826.                 
  827.                 ASSERT(tok1 && tok2);
  828.                 
  829.                 lev2 = NULL;
  830.                 lev1 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3));
  831.                 if (idx->list == NULL)
  832.                     idx->list = lev1;
  833.                 else
  834.                 {
  835.                     ErrMsgNode *t0=NULL, *t1;
  836.                     for (t1=idx->list; TRUE; t1=t1->next)
  837.                     {
  838.                         if (t1==NULL || lev1->code < t1->code)
  839.                         {
  840.                             if (t0 == NULL)
  841.                                 idx->list = lev1;
  842.                             else
  843.                                 t0->next = lev1;
  844.                             lev1->next = t1;
  845.                             break;
  846.                         }
  847.                         if (lev1->code == t1->code)
  848.                         {
  849.                             ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum); 
  850.                             break;
  851.                         }
  852.                         t0 = t1;
  853.                     }
  854.                 }
  855.                 /*any_text = FALSE;*/
  856.             }
  857.             else if (line[0]=='$' && line[1]=='^')
  858.             {
  859.                 /*** NEED ERROR CHECKING HERE ***/
  860.                 char *tok1 = strtok(line+2,", \t\r\n");
  861.                 char *tok2 = strtok(NULL,", \t\r\n");
  862.                 char *tok3 = strtok(NULL,", \t\r\n");
  863.                 
  864.                 ASSERT(tok1 && tok2);
  865.                 
  866.                 lev2 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3));
  867.                 if (lev1->list == NULL)
  868.                     lev1->list = lev2;
  869.                 else
  870.                 {
  871.                     ErrMsgNode *t0=NULL, *t1;
  872.                     for (t1=lev1->list; TRUE; t1=t1->next)
  873.                     {
  874.                         if (t1==NULL || lev2->code < t1->code)
  875.                         {
  876.                             if (t0 == NULL)
  877.                                 lev1->list = lev2;
  878.                             else
  879.                                 t0->next = lev2;
  880.                             lev2->next = t1;
  881.                             break;
  882.                         }
  883.                         if (lev2->code == t1->code)
  884.                         {
  885.                             ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum); 
  886.                             break;
  887.                         }
  888.                         t0 = t1;
  889.                     }
  890.                 }
  891.                 /*any_text = FALSE;*/
  892.             }
  893.             else if (line[0] == '#')
  894.             {
  895.                 if (!any_comt)
  896.                 {
  897.                     cofs = tmpofs;
  898.                     any_comt = TRUE;
  899.                 }
  900.             }
  901.             else
  902.             {
  903.                 if (any_comt)
  904.                 {
  905.                     ErrMsgNode *n = lev2 ? lev2 : lev1;
  906.                     if (n != NULL)
  907.                     {
  908.                         n->cofs = cofs;
  909.                         n->clen = tmpofs - cofs;
  910.                     }
  911.                 }
  912.                 if (!any_text)
  913.                 {
  914.                     /* if (not a blank line) */
  915.                         tofs = tmpofs;
  916.                         any_text = TRUE;
  917.                 }
  918.             }
  919.             tmpofs = ftell(fd);
  920.         }
  921.         
  922.         if (any_text)
  923.         {
  924.             ErrMsgNode *n = lev2 ? lev2 : lev1;
  925.             if (n != NULL)
  926.             {
  927.                 n->tofs = tofs;
  928.                 n->tlen = tmpofs - tofs;
  929.             }
  930.         }
  931.         
  932.         FileClose(fd);
  933.     }
  934.  
  935.     _busy = 0;
  936.     return TRUE;
  937. }
  938.  
  939. /*-------------------------------------------------------------------------
  940.  * new_ErrMsgNode, delete_ErrMsgNode    [Schuler, 12-08-93]
  941.  *
  942.  * Constructor/Destructor for ErrMsgNode
  943.  *
  944.  * MODIFICATIONS
  945.  * 02-02-94 Schuler   Changed arg list to include severity token
  946.  */
  947.  
  948. static ErrMsgNode * new_ErrMsgNode (const char *name, int code, ErrSev sev)
  949. {
  950.     ErrMsgNode *item = (ErrMsgNode*) MemNew(sizeof(ErrMsgNode));
  951.     if (item != NULL)
  952.     {
  953.         item->name = (name) ? StrSave(name) : NULL;
  954.         item->code = code;
  955.         item->sev =sev;
  956.     }
  957.     return item;
  958. }
  959.  
  960. static void delete_ErrMsgNode (ErrMsgNode *item)
  961. {
  962.     if (item != NULL)
  963.     {
  964.         ErrMsgNode *n1, *n2;
  965.         for (n1=item->list; n1; n1=n2)
  966.         {
  967.             n2 = n1->next;
  968.             delete_ErrMsgNode(n1);
  969.         }
  970.         MemFree((void*)item->name);
  971.         MemFree((void*)item->tstr);
  972.         MemFree((void*)item->cstr);
  973.         MemFree((void*)item);
  974.     }
  975. }
  976.  
  977.  
  978. /*-------------------------------------------------------------------------
  979.  * ErrGetMsgRoot    [Schuler, 12-09-93]
  980.  *
  981.  * Gets the index structure for an error context, creating and initializing
  982.  * it if necessary.
  983.  *
  984.  * MODIFICATIONS
  985.  * 01-10-94 Schuler   Changed to call ErrMsgRoot_Read() *after* linking 
  986.  *                    into list.  Otherwise, if an error is posted from
  987.  *                    within ErrMsgRoot_Read(), it won't find the CoreLib
  988.  *                    modlue and will read it again (ad infinitum).
  989.  */
  990.  
  991. ErrMsgRootPtr LIBCALL ErrGetMsgRoot (const char *context)
  992. {
  993.     AppErrInfoPtr info = GetAppErrInfo();
  994.     ErrMsgRoot *idx, *idx0=NULL;
  995.     int d;
  996.     
  997.     for (idx=info->idxlist; TRUE; idx=idx->next)
  998.     {
  999.         if (idx==NULL || (d = strcmp(idx->name,context)) >0)
  1000.         {
  1001.             ErrMsgRoot *idx2 = new_ErrMsgRoot(context);
  1002.             idx2->next = idx;
  1003.             if (idx0 == NULL)
  1004.                 info->idxlist = idx2;
  1005.             else
  1006.                 idx0->next = idx2;
  1007.             ErrMsgRoot_Read(idx2);
  1008.             return (ErrMsgRootPtr) idx2;
  1009.         }
  1010.         if (d == 0)
  1011.             return (ErrMsgRootPtr) idx;
  1012.  
  1013.         idx0 = idx;
  1014.     }
  1015.     return NULL;
  1016. }
  1017.  
  1018.  
  1019. /*-------------------------------------------------------------------------
  1020.  * ErrGetExplanation    [Schuler, 12-09-93]
  1021.  * 
  1022.  * Gets the explanatory text for a particular error from an error 
  1023.  * message file.
  1024.  *
  1025.  * MODIFICATIONS
  1026.  * 02-02-94 Schuler   Fix for memory leak when length==0
  1027.  * 02-02-94 Schuler   Now exported (was static)
  1028.  */
  1029.  
  1030. const char* LIBCALL ErrGetExplanation (ErrMsgRootPtr idx, ErrMsgNodePtr item)
  1031. {
  1032.     if (idx != NULL && item != NULL && item->tlen >0)
  1033.     {
  1034.         size_t bytes;
  1035.         char *text;
  1036.  
  1037.         if (item->tstr != NULL)
  1038.             return item->tstr;
  1039.             
  1040.         bytes = (size_t)item->tlen;
  1041.         if ((text= (char*)MemNew(bytes+1)) != NULL)
  1042.         {
  1043.             FILE *fd = ErrMsgRoot_fopen((ErrMsgRoot *) idx,"rb");
  1044.             if (fd != NULL)
  1045.             {
  1046.                 if (fseek(fd,item->tofs,SEEK_SET) !=0)
  1047.                     goto ErrReturn;
  1048.                 if (fread(text,1,bytes,fd) != bytes)
  1049.                     goto ErrReturn;
  1050.                 *(text+bytes) = '\0';    /* make sure null-terminated */
  1051.                 fclose(fd);
  1052.                 return text;
  1053.             }
  1054.         ErrReturn :
  1055.             fclose(fd);
  1056.             MemFree(text);
  1057.             return NULL;
  1058.         }        
  1059.     }
  1060.     return NULL;
  1061. }
  1062.  
  1063.  
  1064. /***************************************************************************\
  1065. |                             CUSTOMIZATION                                 |
  1066. \***************************************************************************/
  1067.  
  1068. /*-------------------------------------------------------------------------
  1069. * ErrSetLogfile  [Schuler]
  1070. *
  1071. * MODIFICATIONS
  1072. * 07-26-93 Schuler   Modified to use AppErrInfo struct
  1073. * 12-17-93 Schuler   Changed back to returning a Boolean
  1074. * 01-21-94 Schuler   Renamed & added flags argument.
  1075. * 01-21-94 Schuler   Changed fopen/fclose to FileOpen/FileClose
  1076. *
  1077. * TO DO
  1078. * - honor ELOG_NOCREATE flag
  1079. */
  1080.  
  1081. int LIBCALL  Nlm_ErrSetLogfile (const char *filename, unsigned long flags)
  1082. {
  1083.     static char *fmode_append = "a+";
  1084.     static char *fmode_overwrite = "w";
  1085.     
  1086.     AppErrInfoPtr info = GetAppErrInfo();
  1087.     char *fmode = (flags & ELOG_APPEND) ? fmode_append : fmode_overwrite;
  1088.     FILE *fp;
  1089.         
  1090.     if ((fp = FileOpen(filename,fmode)) == NULL)
  1091.         return FALSE;
  1092.     if (flags & ELOG_BANNER)
  1093.     {
  1094.         char buffer[64];
  1095.         int  i;
  1096.  
  1097.         Nlm_DayTimeStr(buffer,TRUE,TRUE);
  1098.         fputc('\n', fp);
  1099.         for (i=0; i<24; i++)  fputc ('=', fp);
  1100.         fprintf(fp, "[ %s ]",buffer);
  1101.         for (i=0; i<24; i++)  fputc ('=', fp);
  1102.         fputc ('\n', fp);
  1103.     }
  1104.     FileClose(fp);
  1105.     
  1106.     strncpy(info->logfile,filename,PATH_MAX);
  1107.     return TRUE;
  1108. }
  1109.  
  1110.  
  1111. /*-------------------------------------------------------------------------
  1112.  * ErrSetHandler    [Schuler, 05-11-93]
  1113.  *
  1114.  * Allows the application to set a hook procedure that will be called
  1115.  * when an error is posted via ErrPost.  It is always called regardless
  1116.  * of any error reporting/logging options that may have been set.  The
  1117.  * return value is the pointer to the previous error hook procedure 
  1118.  * if there was one.
  1119.  * 
  1120.  * MODIFICATIONS
  1121.  * 05-21-93 Schuler   
  1122.  */
  1123. ErrHookProc LIBCALL Nlm_ErrSetHandler (ErrHookProc hookNew)
  1124. {
  1125.     AppErrInfoPtr info = GetAppErrInfo();
  1126.     ErrHookProc hookOld =info->hook;
  1127.     info->hook = hookNew;
  1128.     return hookOld;
  1129. }
  1130.  
  1131.  
  1132. /*-------------------------------------------------------------------------
  1133.  * ErrUserInstall   [Sirotkin]
  1134.  *
  1135.  * Add or replace a user-string.
  1136.  *
  1137.  * MODIFICATIONS
  1138.  * 12-15-93 Schuler   No longer keeps track of string lengths.
  1139.  */
  1140.  
  1141. static char *_strNull = "(null)";
  1142.  
  1143. ErrStrId LIBCALL Nlm_ErrUserInstall (const char *msg, ErrStrId magic_cookie)
  1144. {
  1145.     AppErrInfoPtr info = GetAppErrInfo();
  1146.     ValNode *list = (ValNode *) info->desc.userstr;
  1147.     ValNode *node;
  1148.     ErrStrId cookie;
  1149.     
  1150.     if (msg == NULL)
  1151.         msg = _strNull;
  1152.  
  1153.     if (magic_cookie)
  1154.     {
  1155.         for (node=list; node; node=node->next)
  1156.         {
  1157.             if (node->choice == magic_cookie)
  1158.             {
  1159.                 /** replace **/
  1160.                 info->ustrlen -= strlen((char*)node->data.ptrvalue);
  1161.                 MemFree(node->data.ptrvalue);
  1162.                 node->data.ptrvalue = (void*) StringSave(msg);
  1163.                 info->ustrlen += strlen(msg);
  1164.                 return magic_cookie;
  1165.             }
  1166.         }
  1167.         ErrPostEx(SEV_WARNING,-1,0,
  1168.                 "ErrUserInstall:  bad string id (%d)",
  1169.                 (int) magic_cookie);
  1170.         return 0;
  1171.     }
  1172.     else
  1173.     {
  1174.         for (cookie = 1; cookie < 255; ++cookie)
  1175.         {
  1176.             for (node=list; node; node=node->next)
  1177.             {
  1178.                 if (node->choice == cookie)
  1179.                     break;
  1180.             }
  1181.             if (node==NULL)  /* free cookie is magic */
  1182.             {
  1183.                 node = ValNodeNew((ValNode*)info->desc.userstr);
  1184.                 if ( ! info->desc.userstr)
  1185.                     info->desc.userstr = node;
  1186.                 node->choice = cookie;
  1187.                 node->data.ptrvalue = StringSave(msg);
  1188.                 info->ustrlen += strlen(msg);
  1189.                 info->ustrcnt ++;
  1190.                 return cookie;
  1191.             }
  1192.         }
  1193.         /*ErrPostEx(SEV_WARNING,-1,0,"ErrUserInstall:  no more string id's");*/
  1194.         return 0;
  1195.     }
  1196. }
  1197.  
  1198.  
  1199. /*-------------------------------------------------------------------------
  1200.  * ErrUserDelete   [Sirotkin]
  1201.  *
  1202.  * Delete a single user-string.
  1203.  *
  1204.  * MODIFICATIONS
  1205.  * 12-15-93 Schuler   No longer keeps track of string lengths.
  1206.  */
  1207.  
  1208. Nlm_Boolean LIBCALL Nlm_ErrUserDelete (ErrStrId magic_cookie)
  1209. {
  1210.     AppErrInfoPtr info = GetAppErrInfo();
  1211.     ValNodePtr node = ValNodeExtract((ValNode**)(&info->desc.userstr), magic_cookie);
  1212.  
  1213.     if (node)
  1214.     {
  1215.         ASSERT(node->data.ptrvalue != NULL);
  1216.         info->ustrlen -= strlen((char*)node->data.ptrvalue);
  1217.         info->ustrcnt --;
  1218.         ValNodeFreeData(node);
  1219.         return TRUE;
  1220.     }
  1221.     return FALSE;
  1222. }
  1223.  
  1224.  
  1225. /*-------------------------------------------------------------------------
  1226.  * ErrUserClear   [Sirotkin]
  1227.  *
  1228.  * Deletes the entire list of user-strings.
  1229.  */
  1230. void LIBCALL Nlm_ErrUserClear (void)
  1231. {
  1232.     AppErrInfoPtr info = GetAppErrInfo();
  1233.     ValNodeFreeData((ValNode*)info->desc.userstr);
  1234.     info->ustrlen = 0;
  1235.     info->ustrcnt = 0;
  1236. }
  1237.  
  1238.  
  1239.  
  1240. /*-------------------------------------------------------------------------
  1241.  * ErrGetLogLevel, ErrSetLogLevel   [Schuler, 12-22-93]
  1242.  *
  1243.  * Get/Set the minimum severity level that will be logged.
  1244.  */
  1245. int LIBCALL ErrSetLogLevel (ErrSev level)
  1246. {
  1247.     AppErrInfoPtr info = GetAppErrInfo();
  1248.     int prev = info->opts.log_level;
  1249.     info->opts.log_level = MAX(SEV_INFO,MIN(level,SEV_MAX));
  1250.     return prev;
  1251. }
  1252.  
  1253. int LIBCALL ErrGetLogLevel ()
  1254. {                             
  1255.     return GetAppErrInfo()->opts.log_level;
  1256. }
  1257.  
  1258.  
  1259. /*-------------------------------------------------------------------------
  1260.  * ErrGetMessageLevel, ErrSetMessageLevel   [Schuler, 12-14-93]
  1261.  *
  1262.  * Get/Set the minimum severity level that will be reported via Message().
  1263.  */
  1264. int LIBCALL ErrSetMessageLevel (ErrSev level)
  1265. {
  1266.     AppErrInfoPtr info = GetAppErrInfo();
  1267.     int prev = info->opts.msg_level;
  1268.     info->opts.msg_level = MAX(SEV_INFO,MIN(level,SEV_MAX));
  1269.     return prev;
  1270. }
  1271.  
  1272. int LIBCALL ErrGetMessageLevel ()
  1273. {                             
  1274.     return GetAppErrInfo()->opts.msg_level;
  1275. }
  1276.  
  1277.  
  1278. /*-------------------------------------------------------------------------
  1279.  * ErrGetFatalLevel, ErrSetFatalLevel   [Schuler, 12-14-93]
  1280.  *
  1281.  * Get/Set the minimum severity level that will cause an abnormal exit.
  1282.  */
  1283. int LIBCALL ErrSetFatalLevel (ErrSev level)
  1284. {
  1285.     AppErrInfoPtr info = GetAppErrInfo();
  1286.     int prev = info->opts.die_level;
  1287.     info->opts.die_level = MAX(SEV_INFO,MIN(level,SEV_MAX));
  1288.     return prev;
  1289. }
  1290.  
  1291. int LIBCALL ErrGetFatalLevel ()
  1292. {
  1293.     return GetAppErrInfo()->opts.die_level;
  1294. }
  1295.  
  1296.  
  1297. /*-------------------------------------------------------------------------
  1298.  * ErrSetOptFlags, ErrClearOptFlags, ErrTestOptFlags   [Schuler, 12-14-93]
  1299.  *
  1300.  * Set, clear, or test the current state of any error option flag.
  1301.  */
  1302. unsigned long LIBCALL ErrSetOptFlags (unsigned long flags)
  1303. {
  1304.     AppErrInfoPtr info = GetAppErrInfo();
  1305.     info->opts.flags |= (flags & EO_ALL_FLAGS);
  1306.     return info->opts.flags;
  1307. }
  1308.  
  1309. unsigned long LIBCALL ErrClearOptFlags (unsigned long flags)
  1310. {
  1311.     AppErrInfoPtr info = GetAppErrInfo();
  1312.     info->opts.flags &= ~(flags & EO_ALL_FLAGS);
  1313.     return info->opts.flags;
  1314. }
  1315.  
  1316. unsigned long LIBCALL ErrTestOptFlags (unsigned long flags)
  1317. {
  1318.     AppErrInfoPtr info = GetAppErrInfo();
  1319.     return (info->opts.flags & flags);
  1320. }
  1321.  
  1322.  
  1323. /*-------------------------------------------------------------------------
  1324.  * ErrSaveOptions  [Schuler, 01-03-94]
  1325.  *
  1326.  */
  1327. void LIBCALL ErrSaveOptions (ErrOpts *opts)
  1328. {
  1329.     AppErrInfoPtr info = GetAppErrInfo();
  1330.     
  1331.     ASSERT(opts != NULL);
  1332.     memcpy((void*)opts,(void*)(&info->opts),sizeof(ErrOpts));
  1333. }
  1334.  
  1335.  
  1336. /*-------------------------------------------------------------------------
  1337.  * ErrRestoreOptions  [Schuler, 01-03-94]
  1338.  */
  1339.  
  1340. void LIBCALL ErrRestoreOptions (const ErrOpts *opts)
  1341. {
  1342.     AppErrInfoPtr info = GetAppErrInfo();
  1343.     
  1344.     ASSERT(opts != NULL);
  1345.     memcpy((void*)(&info->opts),(void*)opts,sizeof(ErrOpts));
  1346. }
  1347.  
  1348.  
  1349. /*-------------------------------------------------------------------------
  1350. * ErrGetOpts  [Schuler]
  1351. *
  1352. * MODIFICATIONS
  1353. * 07-26-93 Schuler   Modified to use AppErrInfo struct
  1354. * 02-02-94 Schuler   Added local option saving strategy
  1355. */
  1356.  
  1357. static ErrOpts _eo_save[2];
  1358.  
  1359. void LIBCALL  Nlm_ErrGetOpts (short * erract, short * errlog)
  1360. {
  1361.     AppErrInfoPtr info = GetAppErrInfo();
  1362.     
  1363.     if (erract != NULL)
  1364.     {
  1365.         int i;
  1366.         
  1367.         for (i=0; i<DIM(_eo_save); ++i)
  1368.         {
  1369.             if (_eo_save[i].flags ==0) 
  1370.                 break;
  1371.         }
  1372.         if (i<DIM(_eo_save))
  1373.         {
  1374.             _eo_save[i] = info->opts;
  1375.             *erract = -(1+i);
  1376.         }
  1377.         else
  1378.         {
  1379.             TRACE("ErrGetOpts: overflow\n");
  1380.             *erract = info->opts.actopt;
  1381.         }
  1382.     }
  1383.     if (errlog != NULL)
  1384.         *errlog = info->opts.logopt;
  1385. }
  1386.  
  1387.  
  1388. /*-------------------------------------------------------------------------
  1389. * ErrSetOpts  [Schuler]
  1390. *
  1391. * MODIFICATIONS
  1392. * 07-26-93 Schuler   Modified to use AppErrInfo struct
  1393. * 12-15-93 Schuler   Modified to map old settings to the new options.
  1394. * 12-21-93 Schuler   Now always starts from defaults and then twiddles.
  1395. * 01-27-94 Schuler   Fixed bug whereby logopt not always saved.
  1396. * 02-02-94 Schuler   Added local option saving strategy
  1397. */
  1398.  
  1399.  
  1400. void LIBCALL  Nlm_ErrSetOpts (short actopt, short logopt)
  1401. {
  1402.     AppErrInfoPtr info = GetAppErrInfo();
  1403.     
  1404.     if (actopt < 0)
  1405.     {
  1406.         int i = -(actopt+1);
  1407.         ASSERT(i>=0 && i<DIM(_eo_save));
  1408.         if (_eo_save[i].flags !=0)
  1409.         {
  1410.             info->opts = _eo_save[i];
  1411.             _eo_save[i].flags =0;
  1412.             return;
  1413.         }
  1414.         ErrLogPrintf("*** Improper use of ErrGetOpts/ErrSetOpts ***\n");
  1415.     }
  1416.     else
  1417.     {
  1418.         TRACE("ErrSetOpts: 1st-time call, nothing to restore\n");
  1419.     }
  1420.  
  1421.     /****info->opts.flags = EO_DEFAULTS;
  1422.     info->opts.log_level = SEV_INFO;****/
  1423.     
  1424.     if (actopt !=0)
  1425.         info->opts.flags &= ~EO_PROMPT_ABORT;
  1426.  
  1427.     switch (actopt)
  1428.     {
  1429.         case ERR_CONTINUE:
  1430.         case ERR_IGNORE:
  1431.             info->opts.msg_level = SEV_MAX;
  1432.             info->opts.die_level = SEV_MAX;
  1433.             break;
  1434.         case ERR_TEE:
  1435.             info->opts.msg_level = SEV_WARNING;
  1436.             info->opts.die_level = SEV_MAX;
  1437.             break;
  1438.         case ERR_ADVISE:
  1439.             info->opts.msg_level = SEV_WARNING;
  1440.             info->opts.die_level = SEV_MAX;
  1441.             break;
  1442.         case ERR_ABORT:
  1443.             info->opts.msg_level = SEV_WARNING;
  1444.             info->opts.die_level = SEV_FATAL;
  1445.             break;
  1446.         case ERR_PROMPT:
  1447.             info->opts.msg_level = SEV_WARNING;
  1448.             info->opts.die_level = SEV_FATAL;
  1449.             info->opts.flags |= EO_PROMPT_ABORT;
  1450.             break;
  1451.     }        
  1452.     if (actopt != 0)
  1453.         info->opts.actopt = actopt;
  1454.  
  1455.     
  1456.     switch(logopt)
  1457.     {
  1458.         case ERR_LOG_ON:
  1459.             info->opts.flags |=  EO_LOGTO_USRFILE;
  1460.             break;
  1461.         case ERR_LOG_OFF:
  1462.             info->opts.flags &= ~EO_LOGTO_USRFILE;
  1463.             break;    
  1464.         default:
  1465.             logopt = 0;    
  1466.     }
  1467.     if (logopt != 0)
  1468.         info->opts.logopt = logopt;
  1469.     
  1470.             
  1471.     /***** if anybody liked this, I can put it back...
  1472.     if (logopt != 0)
  1473.     {
  1474.         int   i;
  1475.         FILE *fp;
  1476.         if ((fp = FileOpen(info->logfile, "a+")) != NULL)
  1477.         {
  1478.             fputc ('\n', fp);
  1479.             for (i=0; i<4; i++)  fputc (' ', fp);
  1480.             for (i=0; i<21; i++)  fputc ('-', fp);
  1481.             fprintf (fp, " error logging %sabled ",
  1482.                             logopt==ERR_LOG_ON ? " en" : "dis");
  1483.             for (i=0; i<21; i++)  fputc ('-', fp);
  1484.             fputc ('\n', fp);
  1485.             FileClose(fp);
  1486.         }
  1487.     }
  1488.     *****/
  1489. }
  1490.  
  1491.  
  1492. /***************************************************************************\
  1493. |                               DEBUGGING                                   |
  1494. \***************************************************************************/
  1495.  
  1496.  
  1497. /*-------------------------------------------------------------------------
  1498.  * Trace   [Schuler, 04-13-93]
  1499.  *
  1500.  * Formats a string and sends it to the "trace device" (see the
  1501.  * description of Nlm_TraceStr about "trace device").  Normally, it
  1502.  * is desirable to trace some (possibly verbose) informational messages
  1503.  * during the development phase of a program, but inhibit them in the
  1504.  * final version that is released to end users.  The TRACE macro supports
  1505.  * this style of usage, calling Nlm_Trace only if _DEBUG is defined.
  1506.  * For example:
  1507.  *
  1508.  *        TRACE("!@#$ screwed up (%s,%d)\n",__FILE__,___LINE__);
  1509.  *
  1510.  * Note that it is declared as CDECL (because of the variable argument
  1511.  * list) and may therefore not be callable from some other programming
  1512.  * languages, such as Basic and Pascal.
  1513.  *
  1514.  * MODIFICATIONS
  1515.  * Schuler 07-26-93
  1516.  * Schuler 12-14-93   Merged varargs and stdargs versions of the function
  1517.  */
  1518.  
  1519. #ifdef VAR_ARGS
  1520. void Nlm_Trace (fmt, va_alist)
  1521.  const char *fmt;
  1522.  va_dcl
  1523. #else
  1524. void CDECL  Nlm_Trace (const char *fmt, ...)
  1525. #endif
  1526. {
  1527.     char *buff = GetScratchBuffer();
  1528.     VSPRINTF(buff,fmt);
  1529.     Nlm_TraceStr(buff);
  1530. }
  1531.  
  1532.         
  1533.  
  1534. /*-------------------------------------------------------------------------
  1535. * TraceStr   [Schuler, 04-13-93]
  1536. *
  1537. * Users of the C and C++ should use the TRACE macro and not call this
  1538. * function directly.  This function, unlike Nlm_Trace, it may be called 
  1539. * other programming languages, such as Basic and Pascal.
  1540. *
  1541. * MODIFICATIONS
  1542. * 05-26-93 Schuler  
  1543. * 12-15-93 Schuler   Changed to use TRACE_TO_FILE macro.  
  1544. * 12-15-93 Schuler   Removed tracing to stderr (caused an echo echo ...).
  1545. * 01-21-94 Schuler   Changed fopen/fclose to FileOpen/FileClose
  1546. */
  1547.  
  1548. void LIBCALL Nlm_TraceStr (const char *str)
  1549. {
  1550.     if (str==NULL)  return;
  1551.  
  1552. #ifdef TRACE_TO_STDOUT
  1553.     fprintf(stdout,"%s",str);
  1554. #endif
  1555.  
  1556. #ifdef TRACE_TO_AUX
  1557. #if defined(WIN16) || defined(WIN32)
  1558.     OutputDebugString(str);
  1559. #endif
  1560. #endif /* TRACE_TO_AUX */
  1561.  
  1562. #ifdef TRACE_TO_FILE
  1563.     {
  1564.         FILE *fd = FileOpen("trace.log","a+");
  1565.         if (fd != NULL)
  1566.         {
  1567.             fprintf(fd,"%s",str);
  1568.             FileClose(fd);
  1569.         }
  1570.     }
  1571. #endif /* TRACE_TO_FILE */
  1572. }
  1573.  
  1574.  
  1575. /*-------------------------------------------------------------------------
  1576. * Nlm_AssertionFailed   [Schuler, 04-13-93]
  1577. *
  1578. * Function needed to support the ASSERT and VERIFY macros.
  1579. *
  1580. * MODIFICATIONS
  1581. * 05-11-93 Schuler
  1582. * 12-15-93 Schuler   Use ErrLogPrintf() inseead of Nlm_Trace()
  1583. * 12-15-93 Schuler   Added call to Message() so user knows what happened!
  1584. * 01-21-94 Schuler   Changed MSG_OK to MSG_POST
  1585. */
  1586. void LIBCALL Nlm_AssertionFailed (const char *expression, const char *module,
  1587.                 const char *fname, int linenum)
  1588. {
  1589.     ErrLogPrintf("\nAssertion Failed:  %s\n", expression);
  1590.     if (module != NULL)
  1591.         ErrLogPrintf("  %s",module);
  1592.     if (fname != NULL)
  1593.         ErrLogPrintf("  %s, line %d",fname,linenum);
  1594.     ErrLogPrintStr("\n");
  1595.     Message(MSG_POST,"Assertion Failed:\n%s",expression);
  1596.     AbnormalExit(1);
  1597. }
  1598.  
  1599.  
  1600. /*-------------------------------------------------------------------------
  1601.  * AbnormalExit   [Schuler, 04-13-93]
  1602.  *
  1603.  * Terminates the application immediately.  This should only be done
  1604.  * as a last resort since some (possibly necessary) cleanup code will
  1605.  * not get executed.
  1606.  *
  1607.  * MODIFICATIONS
  1608.  * Schuler  05-11-93
  1609.  * Schuler  07-26-93
  1610.  */
  1611. void LIBCALL  Nlm_AbnormalExit (int code)
  1612. {
  1613.     ErrLogPrintStr("\n***** ABNORMAL PROGRAM EXIT *****\n");
  1614.  
  1615. #ifdef OS_MAC
  1616.     ExitToShell();
  1617. #else
  1618. #ifdef WIN_MSWIN
  1619.     FatalExit(code);
  1620. #else
  1621.     exit(code);
  1622. #endif
  1623. #endif
  1624. }
  1625.  
  1626.  
  1627.  
  1628. /***************************************************************************\
  1629. |                         STATIC HELPER FUNCTIONS                           |
  1630. \***************************************************************************/
  1631.  
  1632. /*-------------------------------------------------------------------------
  1633. * GetAppErrInfo  [Schuler, 07-26-93]
  1634. *
  1635. * MODIFICATIONS
  1636. * 12-13-93 Schuler   Added initialization for new settings.
  1637. * 01-04-94 Schuler   Added code to get settings from ncbi.ini
  1638. * 01-27-94 Schuler   Set & clear _busy when reading from ncbi.ini
  1639. * 02-01-94 Schuler   Updated _flag array
  1640. */
  1641.  
  1642. static char * _file = "ncbi";
  1643. static char * _section = "ErrorProcessing";
  1644.  
  1645. struct FlagInf { char *key; unsigned long bits; };
  1646. static struct FlagInf _flag [] = 
  1647. {
  1648.     "EO_LOG_SEVERITY",  EO_LOG_SEVERITY,
  1649.     "EO_LOG_CODES",     EO_LOG_CODES,
  1650.     "EO_LOG_FILELINE",  EO_LOG_FILELINE,
  1651.     "EO_LOG_USERSTR",   EO_LOG_USERSTR,
  1652.     "EO_LOG_ERRTEXT",   EO_LOG_ERRTEXT,
  1653.     "EO_LOG_MSGTEXT",   EO_LOG_MSGTEXT,
  1654.     "EO_MSG_SEVERITY",  EO_MSG_SEVERITY,
  1655.     "EO_MSG_CODES",     EO_MSG_CODES,
  1656.     "EO_MSG_FILELINE",  EO_MSG_FILELINE,
  1657.     "EO_MSG_USERSTR",   EO_MSG_USERSTR,
  1658.     "EO_MSG_ERRTEXT",   EO_MSG_ERRTEXT,
  1659.     "EO_MSG_MSGTEXT",   EO_MSG_MSGTEXT,
  1660.     "EO_LOGTO_STDOUT",  EO_LOGTO_STDOUT,
  1661.     "EO_LOGTO_STDERR",  EO_LOGTO_STDERR,
  1662.     "EO_LOGTO_TRACE",   EO_LOGTO_TRACE,
  1663.     "EO_LOGTO_USRFILE", EO_LOGTO_USRFILE,
  1664.     "EO_XLATE_CODES",   EO_XLATE_CODES,
  1665.     "EO_WAIT_KEY",      EO_WAIT_KEY,
  1666.     "EO_PROMPT_ABORT",  EO_PROMPT_ABORT,
  1667.     "EO_BEEP",          EO_BEEP
  1668. };
  1669.  
  1670. static struct AppErrInfo * GetAppErrInfo()
  1671. {
  1672.     AppErrInfoPtr info = (AppErrInfoPtr) GetAppProperty(_szPropKey);
  1673.     
  1674.     if (info == NULL)
  1675.     {
  1676.         char buffer[80], *p;
  1677.         int i;
  1678.         
  1679.         info = (struct AppErrInfo*) MemGet(sizeof(struct AppErrInfo), TRUE);
  1680.         if (info == NULL)  
  1681.             Message(MSG_FATAL,"Out of memory");
  1682.         info->opts.actopt = ERR_ABORT;       /* OBSOLETE */
  1683.         info->opts.logopt = ERR_LOG_OFF;     /* OBSOLETE */
  1684.         info->opts.flags = EO_DEFAULTS;
  1685.         info->opts.log_level = SEV_INFO;
  1686.         info->opts.msg_level = SEV_WARNING;
  1687.         info->opts.die_level = SEV_FATAL;
  1688.         strcpy(info->logfile,"error.log");
  1689.  
  1690.         _busy = 1;
  1691.  
  1692.         if (GetAppParam(_file,_section,"MsgPath","",info->msgpath,PATH_MAX -2))
  1693.             FileBuildPath(info->msgpath, NULL, NULL);        
  1694.  
  1695.         for (i=SEV_MIN; i<SEV_MAX; ++i)
  1696.         {
  1697.             GetAppParam(_file,_section,_szSevKey[i],_szSevDef[i],buffer,sizeof buffer);
  1698.             if (buffer[0] == '"')
  1699.             {
  1700.                 if ( (p = strchr(&buffer[1],'"')) != NULL )  *p = '\0';
  1701.                 p = &buffer[1];
  1702.             }
  1703.             else
  1704.             {
  1705.                 p = buffer;
  1706.             }
  1707.             _szSeverity[i] = StrSave(p);
  1708.         }
  1709.  
  1710.         for (i=0; i<DIM(_flag); ++i)
  1711.         {
  1712.             p = _flag[i].key;
  1713.             if (GetAppParam(_file,_section,p,"",buffer,sizeof buffer))
  1714.             {
  1715.                 info->ini_mask |= _flag[i].bits;
  1716.                 if (strchr("1TtYy",buffer[0]))
  1717.                     info->ini_bits |= _flag[i].bits;
  1718.             }
  1719.         }
  1720.         
  1721.         _busy = 0;
  1722.  
  1723.         SetAppProperty(_szPropKey,(void*)info);
  1724.     }
  1725.     return info;
  1726. }
  1727.  
  1728.